package sf.database.jdbc.type;

import sf.common.exception.SmallOrmException;
import sf.common.log.LogUtil;
import sf.tools.reflect.Jdk;

import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public final class TypeHandlerRegistry {

    /**
     * 自定义类型
     */
    private final Map<Type, Map<Integer, TypeHandler<?>>> CUSTOM_TYPE_HANDLER_MAP = new ConcurrentHashMap<>(4);
    /**
     *
     */
    private final Map<Type, Map<Integer, TypeHandler<?>>> TYPE_HANDLER_MAP = new ConcurrentHashMap<>();

    /**
     * 所有的类型转换器
     * key为TypeHandler的class,value为TypeHandler
     */
    private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();

    public TypeHandlerRegistry() {
        init();
    }

    /**
     * 默认初始化方法
     */
    public void init() {

        /**
         * 布尔适配器
         * <p>
         * 对 Oracle，Types.BOOLEAN 对于 setNull 是不工作的
         */
        TypeHandler<Boolean> asBoolean = new BooleanType();
        register(asBoolean);
        register(boolean.class, asBoolean);
        TypeHandler<Boolean> booleanBITType = new BooleanBITType();
        register(booleanBITType);
        register(boolean.class, booleanBITType);
        TypeHandler<Boolean> booleanCharType = new BooleanCharType();
        register(booleanCharType);
        register(boolean.class, booleanCharType);

        /**
         * 字节适配器
         */
        TypeHandler<Byte> asByte = new ByteTINYINTType();
        register(asByte);
        register(byte.class, asByte);

        /**
         * 短整型适配器
         */
        TypeHandler<Short> asShort = new ShortSMALLINTType();
        register(asShort);
        register(short.class, asShort);

        /**
         * 整型适配器
         */
        TypeHandler<Integer> asInteger = new IntegerType();
        register(asInteger);
        register(int.class, asInteger);
        /**
         * 长整适配器
         */
        TypeHandler<Long> asLong = new LongBIGINTType();
        register(asLong);
        register(long.class, asLong);

        /**
         * 浮点适配器
         */
        TypeHandler<Float> asFloat = new FloatREALType();
        register(asFloat);
        register(float.class, asFloat);
        TypeHandler<Float> floatDOUBLEType = new FloatDOUBLEType();
        register(floatDOUBLEType);
        register(float.class, floatDOUBLEType);
        TypeHandler<Float> floatType = new FloatType();
        register(floatType);
        register(float.class, floatType);

        /**
         * 双精度浮点适配器
         */
        TypeHandler<Double> asDouble = new DoubleType();
        register(asDouble);
        register(double.class, asDouble);
        TypeHandler<Double> doubleFLOATType = new DoubleFLOATType();
        register(doubleFLOATType);
        register(double.class, doubleFLOATType);

        /**
         * 大数适配器
         */
        register(new BigDecimalNUMERICType());
        register(new BigDecimalDECIMALType());
        register(new BigIntegerNUMERICType());

        /**
         * 日历适配器
         */
        register(new DateTIMESTAMPType());//时间戳适配器,日期适配器共用
        register(new DateType());//Sql 日期适配器
        register(new DateTIMEType());//Sql 时间适配器
        register(new CalendarTIMESTAMPType());
        register(new DateLongType());//日期---整型转换
        if (Jdk.dateAndTimeApiExists) {
            register(new LocalDateDATEType());
            register(new LocalTimeTIMEType());
            register(new LocalDateTimeTIMESTAMPType());
            register(new InstantTIMESTAMPType());
        }


        /**
         * 字符适配器
         */
        TypeHandler<Character> asChar = new CharacterVARCHARType();
        register(asChar);
        register(char.class, asChar);

        /**
         * 字符串
         */
        TypeHandler<String> asString = new StringVARCHARType();
        register(asString);
        register(CharSequence.class, asString);
        register(new StringClobType());
        register(new StringCHARType());
        register(new StringLONGNVARCHARType());
        register(new StringLONGVARCHARType());
        register(new StringNCHARType());
        register(new StringNCLOBType());
        register(new StringNVARCHARType());
        register(new StringSQLXMLType());

        /**
         * 字节数组适配器
         */
        register(new ByteArrayBINARYType());
        register(new ByteObjectArrayBINARYType());
        register(new BlobByteObjectArrayType());

        /**
         * lob
         */
        register(new BlobType());
        register(new ClobType());
        register(new InputStreamBINARYType());
        register(new ReaderBINARYType());
        register(new FileBlobType());
        register(new FileClobType());

        //////////////////////////// java 对象匹配 ///////////////////
        register(new ObjectJAVAOBJECTType());//默认对象适配器,java对象类型
        register(new ArrayType());
        register(new StructType());
        register(new RowIdType());
        register(new RefType());

        register(new UrlDATALINKType());
    }


    public boolean hasTypeHandler(Class<?> javaType) {
        return hasTypeHandler(javaType, null);
    }


    public boolean hasTypeHandler(Class<?> javaType, Integer sqlType) {
        return javaType != null && getTypeHandler((Type) javaType, sqlType) != null;
    }

    public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
        return ALL_TYPE_HANDLERS_MAP.get(handlerType);
    }

    public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
        return getTypeHandler((Type) type, null);
    }

    public <T> TypeHandler<T> getTypeHandler(Class<T> type, Integer sqlType) {
        return getTypeHandler((Type) type, sqlType);
    }

    public <T> TypeHandler<T> getTypeHandlerNullThrowException(Class<T> type, Integer sqlType) {
        TypeHandler<T> th = getTypeHandler((Type) type, sqlType);
        if (th == null) {
            throw new SmallOrmException("not found handler " + type.getTypeName());
        }
        return th;
    }

    /**
     * @param type
     * @param sqlType
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    private <T> TypeHandler<T> getTypeHandler(Type type, Integer sqlType) {
        Map<Integer, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null && !jdbcHandlerMap.isEmpty()) {
            handler = jdbcHandlerMap.get(sqlType);
            if (handler == null) {
                //第一个是默认的.
                handler = jdbcHandlerMap.entrySet().iterator().next().getValue();
            }
        }
        if (handler == null) {
            handler = ALL_TYPE_HANDLERS_MAP.get(ObjectType.class);
            LogUtil.info("no match handler " + type.getTypeName());
        }
        // type drives generics here
        return (TypeHandler<T>) handler;
    }

    public <T> TypeHandler<T> getCustomTypeHandler(Class<T> type) {
        return getCustomTypeHandler((Type) type, null);
    }

    public <T> TypeHandler<T> getCustomTypeHandler(Class<T> type, Integer sqlType) {
        return getCustomTypeHandler((Type) type, sqlType);
    }

    /**
     * @param type
     * @param sqlType
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    private <T> TypeHandler<T> getCustomTypeHandler(Type type, Integer sqlType) {
        Map<Integer, TypeHandler<?>> jdbcHandlerMap = CUSTOM_TYPE_HANDLER_MAP.get(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null && !jdbcHandlerMap.isEmpty()) {
            handler = jdbcHandlerMap.get(sqlType);
            if (handler == null) {
                //第一个是默认的.
                handler = jdbcHandlerMap.entrySet().iterator().next().getValue();
            }
        }
        if (handler == null) {
//            throw new SmallOrmException("no match handler " + type.getTypeName());
        }
        // type drives generics here
        return (TypeHandler<T>) handler;
    }

    public TypeHandler<?> removeCustomTypeHandler(Type type) {
        Map<Integer, TypeHandler<?>> jdbcHandlerMap = CUSTOM_TYPE_HANDLER_MAP.get(type);
        if (jdbcHandlerMap != null && !jdbcHandlerMap.isEmpty()) {
            TypeHandler<?> th = jdbcHandlerMap.entrySet().iterator().next().getValue();
            jdbcHandlerMap.clear();
            return th;
        }
        return null;
    }

    public TypeHandler<?> registerCustom(Class<?> javaType, Integer sqlType, TypeHandler<?> handler) {
        Map<Integer, TypeHandler<?>> map = CUSTOM_TYPE_HANDLER_MAP.get(javaType);
        if (map == null) {
            map = new LinkedHashMap<>(4);
            CUSTOM_TYPE_HANDLER_MAP.put(javaType, map);
        }
        map.put(sqlType, handler);
        return handler;
    }

    //
    // REGISTER INSTANCE
    //
    public TypeHandler<?> register(TypeHandler<?> handler) {
        return register(handler.getDefaultJavaType(), handler.getSqlType(), handler);
    }

    public <T> void register(Class<T> javaType, TypeHandler<? extends T> typeHandler) {
        register(javaType, typeHandler.getSqlType(), typeHandler);
    }

    public TypeHandler<?> register(Class<?> javaType, Integer sqlType, TypeHandler<?> handler) {
        Map<Integer, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
        if (map == null) {
            map = new LinkedHashMap<>(4);
            TYPE_HANDLER_MAP.put(javaType, map);
        }
        map.put(sqlType, handler);
        ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
        return handler;
    }

    //
    // REGISTER CLASS
    //

    // Only handler type

    // Construct a handler (used also from Builders)

    @SuppressWarnings("unchecked")
    public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
        if (javaTypeClass != null) {
            try {
                Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
                return (TypeHandler<T>) c.newInstance(javaTypeClass);
            } catch (NoSuchMethodException ignored) {
                // ignored
            } catch (Exception e) {
                throw new SmallOrmException("Failed invoking constructor for handler " + typeHandlerClass, e);
            }
        }
        try {
            Constructor<?> c = typeHandlerClass.getConstructor();
            return (TypeHandler<T>) c.newInstance();
        } catch (Exception e) {
            throw new SmallOrmException("Unable to find a usable constructor for " + typeHandlerClass, e);
        }
    }

    // scan


    // get information

    /**
     * @since 3.2.2
     */
    public Collection<TypeHandler<?>> getTypeHandlers() {
        return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
    }
}
